/**
 * \file Dictionnaire.cpp
 * \brief Ce fichier contient une implantation des méthodes de la classe Dictionnaire
 * \author Pierre-Marc Levasseur
 * \version 0.1
 * \date juillet 2014
 *
 */

#include "Dictionnaire.h"
#include<cmath> // pour la fonction abs (valeur absolue)
#include<algorithm> // pour la fonction max et find, et sort
#include<iostream>

using namespace std;

namespace TP2P1
{

	/**
	 * \fn Dictionnaire::Dictionnaire()
	 */
	Dictionnaire::Dictionnaire()
	:racine(0), cpt(0) {}

	/**
	 * \fn Dictionnaire::Dictionnaire(ifstream&)
	 *
	 * \param [in] fichier: flux vers le fichier qu'on veut charger dans le dictionnaire
	 */
	Dictionnaire::Dictionnaire(ifstream& fichier)
	:racine(0), cpt(0)
	{
		string ligne;
		string motOriginal, motTraduit;
		string::iterator posT;
		string::iterator posD;
		string::iterator it;
		string::iterator tabulation; // sera placé sur la tabulation après le mot en anglais
		bool contexte;


		// tant qu'on atteint pas la fin du fichier
		while(!fichier.eof())
		{
			getline(fichier, ligne);

			// lignes d'en-tête
			if(ligne[0] == '#')
				continue;

			// on va chercher le mot original
			tabulation = find(ligne.begin(), ligne.end(), '\t');
			string motOriginal(ligne.begin(), tabulation);

			// on va chercher la position du ~ s'il y en a un
			posT = find(ligne.begin(), ligne.end(), '~');
			// on va chercher la position du ~ s'il y en a un
			posD = find(ligne.begin(), ligne.end(), ':');

			// vérifier s'il y a un contexte entre parenthèses
			contexte = false;
			it = tabulation + 1;
			if(*it == '(')
				contexte = true;

			// passer le contexte s'il y a lieu
			if(contexte == true)
			{
				it = find(it, ligne.end(), ')');
				// pour que l'itérateur it soit sur le premier caractère après la parenthèse
				it++;
			}

			// Traiter les cas comme celui-ci :
			// a	un(e): ~ book = un livre. 2.(instead of number one) ~ year ago; il y a un an[Article]
			if(posD < posT)
			{
				string::iterator temp = find(it, ligne.end(), ':');
				motTraduit = string(it, temp);
			}
			else
			{
				// Traiter les cas comme celui-ci :
				// abode	of no fixed ~ :sans domicile fixe[Noun]
				if(posT < posD && posT != ligne.end() && posD != ligne.end())
				{
					it = find(it, ligne.end(), ':');
					it++; // pour qu'il pointe sur le premier caractère du mot traduit
				}
				//les deux lignes suivantes étaient commentées... j'ai enlevé les commentaires et tout semble marcher, mais c'est à vérifier (SIMON)
				string::iterator temp = find_if(it, ligne.end(), charSpecial);
				motTraduit = string(it, temp);
			}
			try {
				ajouteMot(motOriginal, motTraduit);
			}
			catch (bad_alloc& e)
			{
				_detruire(racine);
				throw;
			}
		}
	}

	/**
	 * \fn Dictionnaire::~Dictionnaire()
	 */
	Dictionnaire::~Dictionnaire()
	{
		_detruire(racine);
		return;
	}

	/**
	 * \fn Dictionnaire::ajouteMot(const string&, const string&)
	 *
	 * \param [in] motOriginal: mot en anglais qu'on ajoute à l'arbre
	 * \param [in] motTraduit: traduction en français de motOriginal
	 */
	void Dictionnaire::ajouteMot(const string& motOriginal, const string& motTraduit)
	{
		if(racine == 0)
		{
			racine = new NoeudDictionnaire(motOriginal);
			racine->traductions.push_back(motTraduit);
			cpt++;
			return;
		}
		_auxAjouteMot(racine, motOriginal, motTraduit);
		_balancer(racine); // pour garder l'arbre balancé à chaque insertion
	}

	/**
	 * \fn Dictionnaire::supprimeMot(const string&)
	 *
	 * \param [in] motOriginal, mot à supprimer du dictionnaire
	 */
	void Dictionnaire::supprimeMot(const string& motOriginal)
	{
		if(cpt == 0)
			throw logic_error("supprimeMot: Le dictionnaire est vide!");

		if(!_auxSupprimeMot(racine, motOriginal))
			throw logic_error("supprimeMot: le mot " + motOriginal + " n'est pas présent dans le dictionnaire.");

		_ajusterHauteur(racine);
		_balancer(racine);
	}

	/**
	 * \fn Dictionnaire::appartient(const string&)
	 *
	 * \param [in] mot: mot qu'on va chercher dans le dictionnaire
	 *
	 * \return true si le mot est dans le dictionnaire, false sinon
	 */
	bool Dictionnaire::appartient(const string & mot)
	{
		return _auxAppartient(racine, mot);
	}

	/**
	 * \fn Dictionnaire::estVide()
	 * \return true si le dictionnaire est vide, false sinon
	 */
	bool Dictionnaire::estVide() const
	{
		return cpt == 0;
	}

	/**
	 * \fn Dictionnaire::afficherSymetrique
	 */
	void Dictionnaire::afficherSymetrique() const
	{
		_auxAfficherSymetrique(racine);
	}

	/**
	*  \fn similitude(const std::string& mot1, const std::string& mot2)
	*  \param[in] mot1 et mot2, deux strings à être comparé
	*  \return un double compris entre 0 et 1 qui représente la similarité entre 2 mots (0=pas similaire du tout, 1=identique)
	*/
	double Dictionnaire::similitude(const std ::string& mot1, const std ::string& mot2)
	{
		//on commence avec similarité parfaite SIMILAR = 1
		double similar = 1;

		//CAS PARTICULIER: deux mots d'une lettre où la lettre est différente
		if (mot1.size()==1 && mot2.size()==1 && mot1.at(0)!=mot2.at(0))
		{
			return 0.25;
		}

		std::string motCourt, motLong;
		//on établit quel mot est le plus court et quel est le plus long
		if(mot1.size()<=mot2.size())
		{
			motCourt = mot1;
			motLong = mot2;
		}
		else
		{
			motCourt = mot2;
			motLong = mot1;
		}
		//on boucle jusqu'à la fin du mot1 (pendant que (SIMILAR>=0) ET qu'il reste des lettres à traiter dans Mot1)
		int distance = motCourt.size();		//distance mesurera la distance entre une lettre recherché dans le deuxieme mot et la lettre dans le mot d'origine
		for(int i = 0; i < (int)motCourt.size(); i++)
		{
			bool trouvee = false; 	//devient true si on trouve la lettre identique à l'indice i du motCourt dans motLong
			//si la lettre de mot1 != la lettre du mot2
			if(motCourt.at(i)!=motLong.at(i))
			{
				//(on boucle à l'intérieur du mot2 pour voir si on peut trouver la lettre.)
				for(int j = 1; j < (int)motLong.size() ; j++)
				{
					//si on la trouve,
					//(rappel: on veux commencer avec les lettres les plus proches et s'éloigner jusqu'à temps qu'on trouve la bonne lettre)

					if(i-j>=0)
					{
						if (motCourt.at(i)==motLong.at(i-j))
						{
							distance = j;
							trouvee=true;
							break;
						}
					}

					else if(i+j < (int)motLong.size())
					{
						if (motCourt.at(i)==motLong.at(i+j))
						{
							distance = j;
							trouvee=true;
							break;
						}
					}
				}
				if (trouvee==true)
				{
					similar = (similar * (min (  abs((double)distance-(double)motCourt.size()) ,  (double)motCourt.size()   )    ) / (double)motCourt.size());
				}
				else
				{
					similar = (similar - ( 1 /(double)motCourt.size() )) ;
				}

			}
			//sinon, ça veut dire que mot1.at(i)==mot2.at(i), et SIMILAR reste égal à sa valeur.
		}
		//on applique une penalité de 10% a SIMILAR pour chaque lettre de difference en longueur
		if(motLong.size()!=motCourt.size())
		{
			double diff = (double)motLong.size()-(double)motCourt.size();
			if (diff>=10)
			{
				similar = 0;
			}
			else
			{
				similar = similar*(1.0 - (diff/10.0) );
			}

		}
		if(similar < 0)
		{
			//SIMILAR = 0;
			similar = 0;
		}

		//retourner SIMILAR
		return similar;
	}

	/**
	 *  \fn Dictionnaire::traduit(const std ::string& mot)
	 *  \param[in] mot: mot a traduire
	 *  \return un vecteur contenant les traductions du mot a traduire
	 */
	std::vector<std::string> Dictionnaire::traduit(const std ::string& mot)
	{
		std::vector<std::string> traduction;
		if(racine==NULL) return traduction;
		_auxTraduit(racine, mot, traduction);
		return traduction;
	}

	void Dictionnaire::_auxTraduit(elem courant, const std::string& mot, std::vector<std::string>& traduction)
	{
		//est-ce que le mot est identique au mot de l'élément courant
		if(mot==courant->mot)
		{
			//si oui, on retourne immédiatement le vecteur de ce noeud
			traduction = courant->traductions;
			return;
		}
//		else
//		{
//			if (courant->gauche!=NULL)			_auxTraduit(courant->gauche, mot, traduction);
//			if (courant->droite!=NULL)			_auxTraduit(courant->droite, mot, traduction);
//		}

		//si le mot est plus petit que le mot du noeud courant
		if(mot<courant->mot)
		{
			//si le fils gauche n'est pas NULL
			if(courant->gauche!=NULL)
			{
				//on vérifie le fils gauche
				_auxTraduit(courant->gauche, mot, traduction);
			}

		}

		//si le mot est plus grand que le mot du noeud courant
		if(mot>courant->mot)
		{
			//si le fils droit n'est pas NULL
			if(courant->droite!=NULL)
			{
				//on vérifie le fils droit
				_auxTraduit(courant->droite, mot, traduction);
			}
		}
		//si on se rend içi, le vecteur est inchangée et la fonction retourne
		return;
	}

	/**
	 *  \fn suggereCorrections(const std::string& motMalEcrit)
	 *  \param[in] motMalEcrit: un string pour lequel il n'y a pas de "match" exact dans le dictionnaire et pour quel on tente de trouver une correction
	 *  \return un vector contenant jusqu'à 10 mots alternatifs
	 */
	std::vector<std::string> Dictionnaire::suggereCorrections(const std ::string& motMalEcrit)
	{
		if(estVide())
		{
			throw logic_error("suggereCorrections: le dictionnaire est vide");
		}
		std::vector< std::pair< std::string, double > > auxSuggestions;		//un vecteur de couple: tient les éléments et leur similarité
		elem courant = racine;
		double minCourantSuggestions;
		_auxSuggereCorrections(courant, auxSuggestions, motMalEcrit, minCourantSuggestions);
		std::vector<std::string> suggestions;
		//changer le vecteur de Pairs en vecteur de mots
		suggestions = _preparerSuggestions(auxSuggestions);
		return suggestions;
	}
	/**
	 *  \fn Dictionnaire::_auxSuggereCorrections(elem courant, std::vector<std::string> suggestions, const std::string& motMalEcrit)
	 *  \param[in] 		courant: le noeud courant qu'on veut traiter
	 *  				suggestions: le vecteur qui contient les corrections suggérées
	 *  				motMalEcrit: le mot mal écrit
	 *  				minCourantSuggestions: la similarité minimal du vecteur
	 *  \param[out] suggestions: le vecteur avec les 10 mots les plus similaires (s'il n'y a pas 10 mots similaires,
	 *  			on retourne le plus de mots similaires possible)
	 */
	void Dictionnaire::_auxSuggereCorrections(elem courant, std::vector< std::pair<std::string,double > >& auxSuggestions, const std ::string& motMalEcrit,
			double& minCourantSuggestions)
	{


		double similarite = similitude(motMalEcrit, courant->mot);		//la similarité du mot courant au motMalEcrit
		//si la similarite est PLUS GRANDE que minCourantSuggestions,
		if(similarite > minCourantSuggestions || auxSuggestions.size() < 10)
		{
			//ajoute le mot courant au vecteur et s'assurer qu'Il ne reste que 10 mots max dans le vecteur
			_auxAjoutSuggestion(auxSuggestions, courant->mot, similarite, minCourantSuggestions);
		}

		//visiter le fils de gauche s'il n'est pas vide
		if(courant->gauche != NULL)
		{
			_auxSuggereCorrections(courant->gauche, auxSuggestions, motMalEcrit, minCourantSuggestions);
		}
		//visiter le fils de droite s'Il n'est pas vide
		if(courant->droite != NULL)
		{
			_auxSuggereCorrections(courant->droite, auxSuggestions, motMalEcrit, minCourantSuggestions);
		}
		return;
	}

	/**
	 *  \fn Dictionnaire::_auxAjoutSuggestion(std::vector<std::pair<std::string, double>& suggestions, std::string motAAjouter, int similarite)
	 *  \param[in] 	suggestions: un vecteur des suggestions actuels et leurs similitude
	 *  			motAAjouter: le mot a ajouter au vecteur
	 *  			similarite: la similarite du mot a ajouter
	 *  			minCourantSuggestions: la similarité minimum courante
	 *  \param[out] le vecteur suggestions modifié
	 */
	void Dictionnaire::_auxAjoutSuggestion(std::vector< std::pair < std::string, double > > & auxSuggestions, std::string motAAjouter, double similarite,
			 double& minCourantSuggestions)
	{
		std::pair<std::string, double> elementAAjouter(motAAjouter, similarite);
		//est-ce que l'item a moins que 10 éléments?
		if (auxSuggestions.size() < 10)
		{
			//si oui, ajouter l'élément
			auxSuggestions.push_back(elementAAjouter);
			//identifier le nouveau minimum
			double calculMinimum= 1.0;
			for(std::vector< std::pair< std::string,double > >::iterator it = auxSuggestions.begin() ; it!=auxSuggestions.end(); it++ )
			{
				if (calculMinimum > it->second)
				{
					calculMinimum = it->second;
				}
			}
			minCourantSuggestions = calculMinimum;
			return;
		}
		else
		{
			int counter=0;
			//trouver l'élément avec la similarité la plus basse du vecteur
			for(std::vector<std::pair<std::string,double > >::iterator it = auxSuggestions.begin() ; it!=auxSuggestions.end(); it++ )
			{
				//si it->second = minimum courant,
				if (it->second == minCourantSuggestions)
				{
					//l'effacer
					auxSuggestions.erase (auxSuggestions.begin()+counter);
					break;
				}
				counter++;
			}

			//ajouter le nouvel élément au vecteur
			auxSuggestions.push_back(elementAAjouter);

			//trouver le nouveau minimum
			double calculMinimum= 1;
			for(std::vector<std::pair<std::string,double > >::iterator it = auxSuggestions.begin() ; it!=auxSuggestions.end(); it++ )
			{
				if (calculMinimum > it->second)
				{
					calculMinimum = it->second;
				}
			}
			minCourantSuggestions = calculMinimum;
			//return
			return;
		}
	}

	/**
	 *  \fn Dictionnaire::_preparerSuggestions(std::vector<std::pair<std::string,double>>& auxSuggestions)
	 *  \param[in] auxSuggestions: un vecteur de pair(string,double) a convertir en vecteur de strings seulement
	 *  \return un vecteur de strings contenant les suggestions
	 */
	std::vector<std::string> Dictionnaire::_preparerSuggestions(std::vector<std::pair<std::string,double > >& auxSuggestions)
	{
		std::vector<std::string> suggestions;
		for(std::vector<std::pair<std::string,double > >::iterator it = auxSuggestions.begin() ; it!=auxSuggestions.end(); it++ )
		{
			suggestions.push_back(it->first);
		}
		return suggestions;
	}




	/**
	 * \fn Dictionnaire::nombreDeMot()
	 * \return retourne le nombre de mot dans le dicitonnaire
	 */
	int Dictionnaire::nombreDeMot() const
	{
		return cpt;
	}

	/**
	 * \fn Dictionnaire::_auxAjouteMot(elem&, const string&, const string&)
	 *
	 * \param [in] n: noeud avec lequel on va comparer le mot reçu
	 * \param [in] motOriginal: mot qu'on cherche à ajouter à l'arbre
	 * \param [in] motTraduit: traduction qu'on veut ajouter à motOriginal
	 */
	void Dictionnaire::_auxAjouteMot(elem& n, const string& motOriginal, const string& motTraduit)
	{
		if(n == 0) // le mot n'est pas dans le dictionnaire, on ajoute un noeud
		{
			n = new NoeudDictionnaire(motOriginal);
			n->traductions.push_back(motTraduit);
			cpt++;
			return;
		}
		// le mot est déjà dans le dicitonnaire, on ajoute une traduciton à ce mot
		else if(n->mot == motOriginal)
		{
			n->traductions.push_back(motTraduit);
			return;
		}
		// on fait un appel récursif pour insérer le mot
		else
		{
			if(n->mot > motOriginal)
			{
				_auxAjouteMot(n->gauche, motOriginal, motTraduit);
			}
			else
			{
				_auxAjouteMot(n->droite, motOriginal, motTraduit);
			}
			n->hauteur = 1 + max(_hauteur(n->gauche), _hauteur(n->droite));
		}
	}

	/**
	 * \fn Dictionnaire::_auxSupprimeMot(elem&, const string&)
	 *
	 * \param [in] n: noeud dans lequel on fait la recherche
	 * \param [in] mot: mot qu'on cherche à retirer du dictionnaire
	 *
	 * \return true si on trouve et on supprime le mot, false sinon
	 */
	bool Dictionnaire::_auxSupprimeMot(elem& n, const string& mot)
	{
		if(n == NULL)
			return false;
		else if (n->mot > mot)
			return _auxSupprimeMot(n->gauche, mot);
		else if (n->mot < mot)
			return _auxSupprimeMot(n->droite, mot);
		else // on a trouvé le mot
		{
			if(n->droite != 0 && n->gauche != 0) // si le noeud à deux enfants
			{
				elem temp = _noeudMinimum(n->droite);
				n->mot = temp->mot;
				n->traductions = temp->traductions;
				_retireMinimum(n->droite);
				return true;
			}
			else // le noeud à aucun enfant ou un seul enfant
			{
				elem temp = n;
				n = n->gauche == NULL ? n->droite : n->gauche;
				delete temp;
				cpt--;
				return true;
			}
		}
	}

	/**
	 * \fn Dictionnaire::_hauteur(elem&)
	 *
	 * \param [in] n: pointeur sur un NoeudDictionnaire
	 *
	 * \return la hauteur du noeud pointé par n, si n pointe sur rien, on renvoie -1
	 */
	int Dictionnaire::_hauteur(elem& n)
	{
		return n == 0 ? -1 : n->hauteur;
	}

	/**
	 * \fn Dictionnaire::_balancer(elem&)
	 *
	 * \param [in] n: pointeur sur le noeud qu'on veut balancer
	 */
	void Dictionnaire::_balancer(elem& n)
	{
		if(n == NULL)
			return;
		_balancer(n->droite);
		_balancer(n->gauche);
		if(abs(_hauteur(n->gauche) - _hauteur(n->droite)) == 2) // s'il y a un débalancement
		{
			if(_hauteur(n->gauche) > _hauteur(n->droite)) // débalancement à gauche
			{
				elem nGauche = n->gauche;
				if(_hauteur(nGauche->gauche) > _hauteur(nGauche->droite))
					_zigZigGauche(n);
				else
					_zigZagGauche(n);
			}
			else // débalancement à droite
			{
				elem nDroit = n->droite;
				if(_hauteur(nDroit->droite) > _hauteur(nDroit->gauche))
					_zigZigDroit(n);
				else
					_zigZagDroit(n);
			}
		}

	}

	/**
	 * \fn Dictionnaire::_zigZigGauche(elem&)
	 *
	 * \param [in] n, noeud à partir duquel on veut faire un rotation
	 */
	void Dictionnaire::_zigZigGauche(elem& n)
	{
		elem temp = n->gauche;
		n->gauche = temp->droite;
		temp->droite = n;
		n->hauteur = 1 + max(_hauteur(n->gauche), _hauteur(n->droite));
		temp->hauteur = 1 + max(_hauteur(temp->gauche), n->hauteur);
		n = temp;
	}

	/**
	 * \fn Dictionnaire::_zigZigDroit(elem&
	 *
	 * \param [in] n, noeud à partir duquel on veut faire une rotation
	 */
	void Dictionnaire::_zigZigDroit(elem& n)
	{
		elem temp = n->droite;
		n->droite = temp->gauche;
		temp->gauche = n;
		n->hauteur = 1 + max(_hauteur(n->gauche), _hauteur(n->droite));
		temp->hauteur = 1 + max(_hauteur(n->droite), n->hauteur);
		n = temp;
	}

	/**
	 * \fn Dictionnaire::_zigZagGauche(elem&)
	 *
	 * \param [in] n, noeud à partir duquel on veut faire les rotations
	 */
	void Dictionnaire::_zigZagGauche(elem& n)
	{
		_zigZigDroit(n->gauche);
		_zigZigGauche(n);
	}

	/**
	 * \fn Dictionnaire::_zigZagDroit(elem&)
	 *
	 * \param [in] n, noeud à partir duquel on veut faire les rotations
	 */
	void Dictionnaire::_zigZagDroit(elem& n)
	{
		_zigZigGauche(n->droite);
		_zigZigDroit(n);
	}

	/**
	 * \fn Dictionnaire::_auxAfficherSymetrique(elem)
	 *
	 * \param [in] n, noeud qu'on veut traiter
	 */
	void Dictionnaire::_auxAfficherSymetrique(elem n) const
	{
		if(n == NULL)
			return;
		_auxAfficherSymetrique(n->gauche);
		cout << n->mot << "  : Hauteur -> " << n->hauteur << endl;
//		ici - AJOUT POUR TEST (SIMON)
//		vector<string> trad = n->traductions;
//		cout<<"Traductions:"<<endl;
//		for(vector<string>::iterator it = trad.begin(); it!=trad.end(); it++)
//		{
//			cout<<*it<<endl;
//		}
//		arret - AJOUT POUR TEST (SIMON)
		_auxAfficherSymetrique(n->droite);
	}

	/**
	 * \fn Dictionnaire::_noaudMinimum(elem&)
	 *
	 * \param [in] n: noeud à partir duquel on va chercher le minimum
	 *
	 * \return un pointeur sur le noeud minimum
	 */
	typename Dictionnaire::elem& Dictionnaire::_noeudMinimum(elem& n)
	{
		if(n->gauche == NULL)
			return n;
		else
			return _noeudMinimum(n->gauche);
	}

	/**
	 * \fn Dictionnaire::_retireMinimum(elem&)
	 *
	 * \param [in] n: noeud à partir duquel on va chercher le noeud minimum pour le supprimer
	 */
	void Dictionnaire::_retireMinimum(elem& n)
	{
		if(n->gauche->gauche == NULL)
		{
			elem temp = n->gauche;
			if(temp->droite != 0)
				n->gauche = temp->droite;
			else
				n->gauche = NULL;
			delete temp;
			cpt--;
			return;
		}
		else
			_retireMinimum(n->gauche);
	}

	/**
	 * \fn Dictionnaire::_ajusterHauteur(elem&)
	 *
	 * \param [in] n: noeud à partir duquel on ajuste la hauteur
	 *
	 * \return la hauteur ajusté du noeud passé en argument
	 */
	int Dictionnaire::_ajusterHauteur(elem& n)
	{
		if(n == NULL)
			return -1;
		else
			n->hauteur = 1 + max(_ajusterHauteur(n->gauche), _ajusterHauteur(n->droite));
		return n->hauteur;
	}

	/**
	 * \fn Dictionnaire::_auxAppartient(elem&, const string&)
	 *
	 * \param [in] n: noeud à partir duquel on va chercher le mot
	 * \param [in] mot: mot qu'on cherche à trouver
	 *
	 * \return true si le mot correspond au noeud, false sinon
	 */
	bool Dictionnaire::_auxAppartient(elem& n, const string& mot) const
	{
		if(n == NULL)
			return false;
		else if (n->mot == mot)
			return true;
		else
			return _auxAppartient(n->gauche, mot) || _auxAppartient(n->droite, mot);
	}

	/**
	 * \fn Dictionnaire::_detruire(elem&)
	 *
	 * \param [in] n: noeud qu'on veut détruire
	 */
	void Dictionnaire::_detruire(elem& n)
	{
		if(n == 0)
			return;
		_detruire(n->gauche);
		_detruire(n->droite);
		n->gauche = 0;
		n->droite = 0;
		delete n;
		cpt--;
		n = 0;
	}

	/**
	 * \fn Dictionnaire::charSpecial
	 *
	 * \param [in] c: caractère qu'on va comparer
	 *
	 * \return true si le caractère est égal à un caractère spécial, false sinon
	 */
	bool charSpecial(char c)
	{
		return c == '(' || c == '[' || c == ',' || c == ';' || c == '\n';
	}



} //Fin du namespace



















